/*---------------------------------------------------------------------------
  Copyright 2020-2021, Microsoft Research, Daan Leijen.

  This is free software; you can redistribute it and/or modify it under the
  terms of the Apache License, Version 2.0. A copy of the License can be
  found in the LICENSE file at the root of this distribution.
---------------------------------------------------------------------------*/
#define __USE_MINGW_ANSI_STDIO 1  // so %z is valid on mingw
#include <stdio.h>
#include "time.h"
#include "kklib.h"
#include <math.h>
#include <limits.h>
#include <float.h>

#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic ignored "-Woverlength-strings"


//define_string_literal(static, stest, 5, "hello")

#define define_singleton(decl,tp,name,tag) \
   decl tp _static_##name = { { KK_HEADER_STATIC(0,tag) } }; \
   decl tp* name = &_static_##name;

// type data1/list
typedef struct __data1_list_s* __data1__list;
struct __data1_list_s {
  kk_block_t _block;
};

define_singleton(static, struct __data1_list_s, __data1_singleton_Nil, (kk_tag_t)0)

struct __data1_Cons {
  struct __data1_list_s _inherit;
  kk_box_t x;
  __data1__list tail;
};
static inline bool __data1__is_Cons(__data1__list x) {
  return (x != __data1_singleton_Nil);
}
static __data1__list __data1__new_Cons(kk_box_t x, __data1__list tail, kk_context_t* ctx) {
  struct __data1_Cons* _con = kk_block_alloc_as(struct __data1_Cons, 2 /* scan fields */, (kk_tag_t)1 /* tag */, ctx);
  _con->x = x;
  _con->tail = tail;
  return &_con->_inherit;
}
static struct __data1_Cons* __data1__as_Cons(__data1__list x) {
  assert(__data1__is_Cons(x));
  return (struct __data1_Cons*)(&x->_block);
}

static msecs_t test_timing(const char* msg, size_t loops, void (*fun)(kk_integer_t, kk_integer_t), kk_integer_t x, kk_integer_t y, kk_context_t* ctx) {
  kk_unused(msg);
  msecs_t start = _clock_start();
  for (size_t i = 0; i < loops; i++) {
    fun(kk_integer_dup(x, ctx), kk_integer_dup(y, ctx));
  }
  msecs_t end = _clock_end(start);
  kk_integer_drop(x, ctx);
  kk_integer_drop(y, ctx);
  //printf("test %s, %zu iterations: %6.3f s\n", msg, loops, (double)(end)/1000.0);
  return end;
}

typedef kk_integer_t(iop)(kk_integer_t x, kk_integer_t y, kk_context_t* ctx);
typedef intptr_t(xop)(intptr_t x, intptr_t y, kk_context_t* ctx);


static intptr_t check(intptr_t z) { if (z < KK_SMALLINT_MIN || z > KK_SMALLINT_MAX) return 10; return z; }
static intptr_t add(intptr_t x, intptr_t y, kk_context_t* ctx) { kk_unused(ctx);  return check(x + y); }
static intptr_t sub(intptr_t x, intptr_t y, kk_context_t* ctx) { kk_unused(ctx); return check(x - y); }
static intptr_t mul(intptr_t x, intptr_t y, kk_context_t* ctx) { kk_unused(ctx); return check(x * y); }

static void testx(const char* name, iop* op, xop* opx, kk_intf_t i, kk_intf_t j, kk_context_t* ctx) {
  kk_integer_t x = _kk_new_integer(i);
  kk_integer_t y = _kk_new_integer(j);
  intptr_t k = _kk_integer_value(op(x, y, ctx));
  intptr_t expect = opx(i, j, ctx);
  printf("%16zx %s %16zx = %16zx: %4s   (expected %zx) %s\n", (intptr_t)i, name, (intptr_t)j, k, (k == expect ? "ok" : "FAIL"), expect, (k == 10 ? "(overflow)" : ""));
}
static void testb(const char* name, iop* op, kk_integer_t x, kk_integer_t y, kk_integer_t expect, kk_context_t* ctx) {
  kk_integer_t k = (op(x, y, ctx));
  printf("%16zx %s %16zx = %16zx: %4s   (expected %zx) %s\n", (intptr_t)_kk_integer_value(x), name, (intptr_t)_kk_integer_value(y), (intptr_t)_kk_integer_value(k), (_kk_integer_value(k) == _kk_integer_value(expect) ? "ok" : "FAIL"), (intptr_t)_kk_integer_value(expect), (_kk_integer_value(k) == 43 ? "(overflow)" : ""));
}
static void test_op(const char* name, iop* op, xop* opx, kk_context_t* ctx) {
  testx(name, op, opx, KK_SMALLINT_MAX, 1, ctx);
  testx(name, op, opx, 0, 0, ctx);
  testx(name, op, opx, 1, 0, ctx);
  testx(name, op, opx, -1, 0, ctx);
  testx(name, op, opx, 0, 1, ctx);
  testx(name, op, opx, 0, 0, ctx);
  testx(name, op, opx, 0, -1, ctx);
  testx(name, op, opx, KK_SMALLINT_MAX, 0, ctx);
  testx(name, op, opx, KK_SMALLINT_MAX, -1, ctx);
  testx(name, op, opx, KK_SMALLINT_MIN, 1, ctx);
  testx(name, op, opx, KK_SMALLINT_MIN, 0, ctx);
  testx(name, op, opx, KK_SMALLINT_MIN, -1, ctx);
  testx(name, op, opx, KK_SMALLINT_MAX, KK_SMALLINT_MAX, ctx);
  testx(name, op, opx, KK_SMALLINT_MAX, KK_SMALLINT_MIN, ctx);
  testx(name, op, opx, KK_SMALLINT_MIN, KK_SMALLINT_MAX, ctx);
  testx(name, op, opx, KK_SMALLINT_MIN, KK_SMALLINT_MIN, ctx);
  testb(name, op, _kk_new_integer(24), _kk_new_integer(24), _kk_new_integer(41), ctx);  // ptr + ptr
  testb(name, op, _kk_new_integer(24), _kk_new_integer(13), _kk_new_integer(41), ctx);  // ptr + int
  testb(name, op, _kk_new_integer(13), _kk_new_integer(24), _kk_new_integer(41), ctx);  // int + ptr
}

static void test(kk_context_t* ctx) {
  test_op("+", &kk_integer_add, &add, ctx);
  test_op("-", &kk_integer_sub, &sub, ctx);
  test_op("*", &kk_integer_mul, &mul, ctx);
}

static void test_add(kk_integer_t x, kk_integer_t y, kk_integer_t expect, kk_context_t* ctx) {
  kk_integer_dup(x, ctx); kk_integer_dup(y, ctx);
  kk_integer_print(x, ctx); printf(" + "); kk_integer_print(y, ctx); printf(" = ");
  kk_integer_t z = kk_integer_add(x, y, ctx);
  kk_integer_print(z, ctx); printf(", expected: ");
  kk_integer_print(expect, ctx);
  printf("\n");
}

static void test_sub(kk_integer_t x, kk_integer_t y, kk_integer_t expect, kk_context_t* ctx) {
  kk_integer_dup(x, ctx); kk_integer_dup(y, ctx);
  kk_integer_print(x, ctx); printf(" - "); kk_integer_print(y, ctx); printf(" = ");
  kk_integer_t z = kk_integer_sub(x, y, ctx);
  kk_integer_print(z, ctx); printf(", expected: ");
  kk_integer_print(expect, ctx);
  printf("\n");
}

static void fibx(int n, kk_integer_t* x1, kk_integer_t* x2, kk_context_t* ctx) {
  if (n <= 1) {
    *x1 = kk_integer_zero;
    *x2 = kk_integer_zero;
  }
  else if (n == 2) {
    *x1 = kk_integer_one;
    *x2 = kk_integer_zero;
  }
  else {
    kk_integer_t y1;
    kk_integer_t y2;
    fibx(n - 1, &y1, &y2, ctx);
    *x2 = y1; kk_integer_dup(y1, ctx);
    *x1 = kk_integer_add(y1, y2, ctx);
  }
}

static kk_integer_t fib(int n, kk_context_t* ctx) {
  kk_integer_t y1;
  kk_integer_t y2;
  fibx(n + 1, &y1, &y2, ctx);
  kk_integer_drop(y2, ctx);
  return y1;
}



static void test_fib(int i, kk_context_t* ctx) {
  printf("fib %i = ", i);
  kk_integer_print(fib(i, ctx), ctx);
  printf("\n");
}

static void test_read(const char* s, kk_context_t* ctx) {
  printf("read %s = ", s);
  kk_integer_print(kk_integer_from_str(s, ctx), ctx);
  printf("\n");
}

static void expect(bool b, bool exp) {
  kk_unused_release(b); kk_unused_release(exp);
  assert(b == exp);
}

static void expect_eq(kk_integer_t x, kk_integer_t y, kk_context_t* ctx) {
  kk_integer_dup(x, ctx); kk_integer_dup(y, ctx);
  printf(" "); kk_integer_print(x, ctx); printf(" == ");  kk_integer_print(y, ctx);
  bool eq = kk_integer_eq(x, y, ctx);
  printf(" %s\n", (eq ? "ok" : "FAIL"));
  assert(eq);
}

static void test_cmp_pos(kk_context_t* ctx) {
  printf("cmp works for positive numbers?\n");

  expect(kk_integer_gt(kk_integer_from_small(54), kk_integer_from_small(45), ctx), true);
  expect(kk_integer_gt(kk_integer_from_small(45), kk_integer_from_small(54), ctx), false);
  expect(kk_integer_gt(kk_integer_from_small(45), kk_integer_from_small(45), ctx), false);
  expect(kk_integer_gt(kk_integer_from_str("5498765432109876", ctx), kk_integer_from_str("4598765432109876", ctx), ctx), true);
  expect(kk_integer_gt(kk_integer_from_str("4598765432109876", ctx), kk_integer_from_str("5498765432109876", ctx), ctx), false);
  expect(kk_integer_gt(kk_integer_from_str("4598765432109876", ctx), kk_integer_from_str("4598765432109876", ctx), ctx), false);
}

static void test_addx(kk_context_t* ctx) {
  printf("addition\n");
  expect_eq(kk_integer_add(kk_integer_from_small(0), kk_integer_from_str("9844190321790980841789", ctx), ctx), kk_integer_from_str("9844190321790980841789", ctx), ctx);
  expect_eq(kk_integer_add(kk_integer_from_str("9844190321790980841789", ctx), kk_integer_from_small(0), ctx), kk_integer_from_str("9844190321790980841789", ctx), ctx);
  expect_eq(kk_integer_add(kk_integer_from_small(0), kk_integer_from_str("-9844190321790980841789", ctx), ctx), kk_integer_from_str("-9844190321790980841789", ctx), ctx);
  expect_eq(kk_integer_add(kk_integer_from_str("-9844190321790980841789", ctx), kk_integer_from_small(0), ctx), kk_integer_from_str("-9844190321790980841789", ctx), ctx);
  expect_eq(kk_integer_add(kk_integer_from_str("-9007199254740983", ctx), kk_integer_from_str("-9999999999999998", ctx), ctx), kk_integer_from_str("-19007199254740981", ctx), ctx);

  expect_eq(kk_integer_add(kk_integer_from_str("1234567890987654321", ctx), kk_integer_from_str("9876543210123456789", ctx), ctx), kk_integer_from_str("11111111101111111110", ctx), ctx);
  expect_eq(kk_integer_add(kk_integer_from_str("1234567890987654321", ctx), kk_integer_from_str("-9876543210123456789", ctx), ctx), kk_integer_from_str("-8641975319135802468", ctx), ctx);
  expect_eq(kk_integer_add(kk_integer_from_str("-1234567890987654321", ctx), kk_integer_from_str("9876543210123456789", ctx), ctx), kk_integer_from_str("8641975319135802468", ctx), ctx);
  expect_eq(kk_integer_add(kk_integer_from_str("-1234567890987654321", ctx), kk_integer_from_str("-9876543210123456789", ctx), ctx), kk_integer_from_str("-11111111101111111110", ctx), ctx);
  expect_eq(kk_integer_add(kk_integer_from_str("9876543210123456789", ctx), kk_integer_from_str("1234567890987654321", ctx), ctx), kk_integer_from_str("11111111101111111110", ctx), ctx);
  expect_eq(kk_integer_add(kk_integer_from_str("9876543210123456789", ctx), kk_integer_from_str("-1234567890987654321", ctx), ctx), kk_integer_from_str("8641975319135802468", ctx), ctx);
  expect_eq(kk_integer_add(kk_integer_from_str("-9876543210123456789", ctx), kk_integer_from_str("1234567890987654321", ctx), ctx), kk_integer_from_str("-8641975319135802468", ctx), ctx);
  expect_eq(kk_integer_add(kk_integer_from_str("-9876543210123456789", ctx), kk_integer_from_str("-1234567890987654321", ctx), ctx), kk_integer_from_str("-11111111101111111110", ctx), ctx);

  expect_eq(kk_integer_sub(kk_integer_from_str("1234567890987654321", ctx), kk_integer_from_str("9876543210123456789", ctx), ctx), kk_integer_from_str("-8641975319135802468", ctx), ctx);
  expect_eq(kk_integer_sub(kk_integer_from_str("1234567890987654321", ctx), kk_integer_from_str("-9876543210123456789", ctx), ctx), kk_integer_from_str("11111111101111111110", ctx), ctx);
  expect_eq(kk_integer_sub(kk_integer_from_str("-1234567890987654321", ctx), kk_integer_from_str("9876543210123456789", ctx), ctx), kk_integer_from_str("-11111111101111111110", ctx), ctx);
  expect_eq(kk_integer_sub(kk_integer_from_str("-1234567890987654321", ctx), kk_integer_from_str("-9876543210123456789", ctx), ctx), kk_integer_from_str("8641975319135802468", ctx), ctx);
  expect_eq(kk_integer_sub(kk_integer_from_str("9876543210123456789", ctx), kk_integer_from_str("1234567890987654321", ctx), ctx), kk_integer_from_str("8641975319135802468", ctx), ctx);
  expect_eq(kk_integer_sub(kk_integer_from_str("9876543210123456789", ctx), kk_integer_from_str("-1234567890987654321", ctx), ctx), kk_integer_from_str("11111111101111111110", ctx), ctx);
  expect_eq(kk_integer_sub(kk_integer_from_str("-9876543210123456789", ctx), kk_integer_from_str("1234567890987654321", ctx), ctx), kk_integer_from_str("-11111111101111111110", ctx), ctx);
  expect_eq(kk_integer_sub(kk_integer_from_str("-9876543210123456789", ctx), kk_integer_from_str("-1234567890987654321", ctx), ctx), kk_integer_from_str("-8641975319135802468", ctx), ctx);
}

#define append10(s) s s s s s s s s s s

static const char* a = "1234567890";
static const char* b = append10("1234567890");
static const char* c = append10(append10("1234567890"));
// static const char* d = append10(append10(append10("1234567890")));
// static const char* e = append10(append10(append10(append10("1234567890"))));


static void test_carry(kk_context_t* ctx) {
  const char* fibs[] = { "1", "1", "2", "3", "5", "8", "13", "21", "34", "55", "89", "144", "233", "377", "610", "987", "1597", "2584", "4181", "6765", "10946", "17711", "28657", "46368", "75025", "121393", "196418", "317811", "514229", "832040", "1346269", "2178309", "3524578", "5702887", "9227465", "14930352", "24157817", "39088169", "63245986", "102334155", "165580141", "267914296", "433494437", "701408733", "1134903170", "1836311903", "2971215073", "4807526976", "7778742049", "12586269025"
                       , "20365011074", "32951280099", "53316291173", "86267571272", "139583862445", "225851433717", "365435296162", "591286729879", "956722026041", "1548008755920", "2504730781961", "4052739537881", "6557470319842", "10610209857723", "17167680177565", "27777890035288", "44945570212853", "72723460248141", "117669030460994", "190392490709135", "308061521170129", "498454011879264", "806515533049393", "1304969544928657", "2111485077978050", "3416454622906707", "5527939700884757", "8944394323791464", "14472334024676221", "23416728348467685", "37889062373143906", "61305790721611591", "99194853094755497", "160500643816367088", "259695496911122585", "420196140727489673", "679891637638612258", "1100087778366101931", "1779979416004714189", "2880067194370816120", "4660046610375530309", "7540113804746346429", "12200160415121876738", "19740274219868223167", "31940434634990099905", "51680708854858323072", "83621143489848422977", "135301852344706746049", "218922995834555169026"
  };
  kk_integer_t num = kk_integer_from_small(1);
  kk_integer_t last = kk_integer_from_small(1);

  for (intptr_t i = 2; i < 99; i++) {
    kk_integer_dup(last, ctx);
    num = kk_integer_add(num, last, ctx);

    kk_integer_dup(num, ctx);
    last = kk_integer_sub(num, last, ctx);

    kk_integer_dup(num, ctx);
    expect_eq(num, kk_integer_from_str(fibs[i], ctx), ctx);
  }
  kk_integer_drop(num, ctx);
  kk_integer_drop(last, ctx);

  expect_eq(kk_integer_add(kk_integer_from_str("9007199254740991", ctx), kk_integer_from_small(1), ctx), kk_integer_from_str("9007199254740992", ctx), ctx);
  expect_eq(kk_integer_add(kk_integer_from_str("999999999999999999999000000000000000000000", ctx), kk_integer_from_str("1000000000000000000000", ctx), ctx), kk_integer_from_str("1e42", ctx), ctx);
  expect_eq(kk_integer_add(kk_integer_from_str("1e20", ctx), kk_integer_from_str("9007199254740972", ctx), ctx), kk_integer_from_str("100009007199254740972", ctx), ctx);
  expect_eq(kk_integer_add(kk_integer_from_str("-9007199254740983", ctx), kk_integer_from_str("-9999999999999998", ctx), ctx), kk_integer_from_str("-19007199254740981", ctx), ctx);

  expect_eq(kk_integer_sub(kk_integer_from_str(c, ctx), kk_integer_add(kk_integer_from_str(b, ctx), kk_integer_from_small(1), ctx), ctx), kk_integer_from_str("1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678899999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", ctx), ctx);
  expect_eq(kk_integer_sub(kk_integer_from_str(b, ctx), kk_integer_add(kk_integer_from_str(c, ctx), kk_integer_from_small(1), ctx), ctx), kk_integer_from_str("-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", ctx), ctx);
  expect_eq(kk_integer_sub(kk_integer_from_str("100000000000000000000000000000000000", ctx), kk_integer_from_str("999999999999999999", ctx), ctx), kk_integer_from_str("99999999999999999000000000000000001", ctx), ctx);
  expect_eq(kk_integer_sub(kk_integer_from_str("10000000010000000", ctx), kk_integer_from_str("10000000", ctx), ctx), kk_integer_from_str("10000000000000000", ctx), ctx);
}

/* Borrow n */
static kk_integer_t factorial(kk_integer_t n, kk_context_t* ctx) {
  // 0 is a small integer and is not reference-counted
  if (kk_integer_eq(n, kk_integer_from_small(0), ctx)) {
    return kk_integer_from_small(1);
  }
  // 1 is a small integer and is not reference-counted
  if (kk_integer_eq(n, kk_integer_from_small(1), ctx)) {
    return kk_integer_from_small(1);
  }
  kk_integer_dup(n, ctx);
  return kk_integer_mul(factorial(kk_integer_dec(kk_integer_dup(n, ctx), ctx), ctx), n, ctx);
}

static void test_large(kk_context_t* ctx) {
  const char* tenFactorial = "3628800";
  const char* hundredFactorial = "93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000";
  const char* threeToTenThousand = "16313501853426258743032567291811547168121324535825379939348203261918257308143190787480155630847848309673252045223235795433405582999177203852381479145368112501453192355166224391025423628843556686559659645012014177448275529990373274425446425751235537341867387607813619937225616872862016504805593174059909520461668500663118926911571773452255850626968526251879139867085080472539640933730243410152186914328917354576854457274195562218013337745628502470673059426999114202540773175988199842487276183685299388927825296786440252999444785694183675323521704432195785806270123388382931770198990841300861506996108944782065015163410344894945809337689156807686673462563038164792190665340124344133980763205594364754963451564072340502606377790585114123814919001637177034457385019939060232925194471114235892978565322415628344142184842892083466227875760501276009801530703037525839157893875741192497705300469691062454369926795975456340236777734354667139072601574969834312769653557184396147587071260443947944862235744459711204473062937764153770030210332183635531818173456618022745975055313212598514429587545547296534609597194836036546870491771927625214352957503454948403635822345728774885175809500158451837389413798095329711993092101417428406774326126450005467888736546254948658602484494535938888656542746977424368385335496083164921318601934977025095780370104307980276356857350349205866078371806065542393536101673402017980951598946980664330391505845803674248348878071010412918667335823849899623486215050304052577789848512410263834811719236949311423411823585316405085306164936671137456985394285677324771775046050970865520893596151687017153855755197348199659070192954771308347627111052471134476325986362838585959552209645382089055182871854866744633737533217524880118401787595094060855717010144087136495532418544241489437080074716158404895914136451802032446707961058757633345691696743293869623745410870051851590672859347061212573446572045088465460616826082579731686004585218284333452396157730036306379421822435818001505905203918209206969662326706952623512427380240468784114535101496733983401240219840048956733689309620321613793757156727562461651933397540266795963865921590913322060572673349849253303397874242381960775337182730037783698708748781738419747698880321601186310506332869704931303076839444790968339306301273371014087248060946851793697973114432706759288546077622831002526800554849696867710280945946603669593797354642136622231192695027321229511912952940320879763123151760555959496961163141455688278842949587288399100273691880018774147568892650186152065335219113072582417699616901995530249937735219099786758954892534365835235843156112799728164123461219817343904782402517111603206575330527850752564642995318064985900815557979945885931124351303252811255254295797082281946658798705979077492469849644183166585950844953164726896146168297808178398470451561320526180542310840744843107469368959707726836608471817060598771730170755446473440774031371227437651048421606224757527085958515947273151027400662948161111284777828103531499488913672800783167888051177155427285103861736658069404797695900758820465238673970882660162285107599221418743657006872537842677883708807515850397691812433880561772652364847297019508025848964833883225165668986935081274596293983121864046277268590401580209059988500511262470167150495261908136688693861324081559046336288963037090312033522400722360882494928182809075406914319957044927504420797278117837677431446979085756432990753582588102440240611039084516401089948868433353748444104639734074519165067632941419347985624435567342072815910754484123812917487312938280670403228188813003978384081332242484646571417574404852962675165616101527367425654869508712001788393846171780457455963045764943565964887518396481296159902471996735508854292964536796779404377230965723361625182030798297734785854606060323419091646711138678490928840107449923456834763763114226000770316931243666699425694828181155048843161380832067845480569758457751090640996007242018255400627276908188082601795520167054701327802366989747082835481105543878446889896230696091881643547476154998574015907396059478684978574180486798918438643164618541351689258379042326487669479733384712996754251703808037828636599654447727795924596382283226723503386540591321268603222892807562509801015765174359627788357881606366119032951829868274617539946921221330284257027058653162292482686679275266764009881985590648534544939224296689791195355783205968492422636277656735338488299104238060289209390654467316291591219712866052661347026855261289381236881063068219249064767086495184176816629077103667131505064964190910450196502178972477361881300608688593782509793781457170396897496908861893034634895715117114601514654381347139092345833472226493656930996045016355808162984965203661519182202145414866559662218796964329217241498105206552200001";

  expect_eq(factorial(kk_integer_from_small(10), ctx), kk_integer_from_str(tenFactorial, ctx), ctx);
  expect_eq(factorial(kk_integer_from_small(100), ctx), kk_integer_from_str(hundredFactorial, ctx), ctx);
  expect_eq(kk_integer_pow(kk_integer_from_small(3), kk_integer_from_int(10000, ctx), ctx), kk_integer_from_str(threeToTenThousand, ctx), ctx);

  // large multiply divide
  kk_integer_t x = kk_integer_from_str(hundredFactorial, ctx);
  expect_eq(kk_integer_cdiv(kk_integer_mul(kk_integer_dup(x, ctx), kk_integer_dup(x, ctx), ctx), kk_integer_dup(x, ctx), ctx), x, ctx);
  x = kk_integer_from_str(threeToTenThousand, ctx);
  expect_eq(kk_integer_cdiv(kk_integer_mul(kk_integer_dup(x, ctx), kk_integer_dup(x, ctx), ctx), kk_integer_dup(x, ctx), ctx), x, ctx);
  kk_integer_t y = kk_integer_from_str(hundredFactorial, ctx);
  x = kk_integer_from_str(threeToTenThousand, ctx);
  expect_eq(kk_integer_cdiv(kk_integer_mul(kk_integer_dup(y, ctx), kk_integer_dup(x, ctx), ctx), y, ctx), x, ctx);
}

static void test_cdiv(kk_context_t* ctx) {
  expect_eq(kk_integer_cdiv(kk_integer_from_str("163500573666152634716420931676158", ctx), kk_integer_from_int(13579, ctx), ctx), kk_integer_from_str("12040693251797086288859336598", ctx), ctx);
  expect_eq(kk_integer_cdiv(kk_integer_from_str("163500573666152634716420931676158", ctx), kk_integer_from_int(-13579, ctx), ctx), kk_integer_from_str("-12040693251797086288859336598", ctx), ctx);
  expect_eq(kk_integer_cdiv(kk_integer_from_str("-163500573666152634716420931676158", ctx), kk_integer_from_int(13579, ctx), ctx), kk_integer_from_str("-12040693251797086288859336598", ctx), ctx);
  expect_eq(kk_integer_cdiv(kk_integer_from_str("-163500573666152634716420931676158", ctx), kk_integer_from_int(-13579, ctx), ctx), kk_integer_from_str("12040693251797086288859336598", ctx), ctx);

  expect_eq(kk_integer_cdiv(kk_integer_from_str("1234567890987654321", ctx), kk_integer_from_str("132435465768798", ctx), ctx), kk_integer_from_str("9322", ctx), ctx);
  expect_eq(kk_integer_cdiv(kk_integer_from_str("1234567890987654321", ctx), kk_integer_from_str("-132435465768798", ctx), ctx), kk_integer_from_str("-9322", ctx), ctx);
  expect_eq(kk_integer_cdiv(kk_integer_from_str("-1234567890987654321", ctx), kk_integer_from_str("132435465768798", ctx), ctx), kk_integer_from_str("-9322", ctx), ctx);
  expect_eq(kk_integer_cdiv(kk_integer_from_str("-1234567890987654321", ctx), kk_integer_from_str("-132435465768798", ctx), ctx), kk_integer_from_str("9322", ctx), ctx);

  expect_eq(kk_integer_cdiv(kk_integer_from_str("786456456335437356436", ctx), kk_integer_from_str("-5423424653", ctx), ctx), kk_integer_from_str("-145011041298", ctx), ctx);
  expect_eq(kk_integer_cdiv(kk_integer_from_str("-93453764643534523", ctx), kk_integer_from_str("-2342", ctx), ctx), kk_integer_from_str("39903400787162", ctx), ctx);
  expect_eq(kk_integer_cdiv(kk_integer_from_str("10000000000000000", ctx), kk_integer_from_str("-10000000000000000", ctx), ctx), kk_integer_from_str("-1", ctx), ctx);

  expect_eq(kk_integer_cdiv(kk_integer_from_str("98789789419609840614360398703968368740365403650364036403645046", ctx), kk_integer_from_small(-1), ctx), kk_integer_from_str("-98789789419609840614360398703968368740365403650364036403645046", ctx), ctx);

  expect_eq(kk_integer_cdiv(kk_integer_from_str("98109840984098409156481068456541684065964819841065106865710397464513210416435401645030648036034063974065004951094209420942097421970490274195049120974210974209742190274092740492097420929892490974202241981098409840984091564810684565416840659648198410651068657103974645132104164354016450306480360340639740650049510942094209420974219704902741950491209742109742097421902740927404920974209298924909742022419810984098409840915648106845654168406596481984106510686571039746451321041643540164503064803603406397406500495109420942094209742197049027419504912097421097420974219027409274049209742092989249097420224198109840984098409156481068456541684065964819841065106865710397464513210416435401645030648036034063974065004951094209420942097421970490274195049120974210974209742190274092740492097420929892490974202241981098409840984091564810684565416840659648198410651068657103974645132104164354016450306480360340639740650049510942094209420974219704902741950491209742109742097421902740927404920974209298924909742022419810984098409840915648106845654168406596481984106510686571039746451321041643540164503064803603406397406500495109420942094209742197049027419504912097421097420974219027409274049209742092989249097420224198109840984098409156481068456541684065964819841065106865710397464513210416435401645030648036034063974065004951094209420942097421970490274195049120974210974209742190274092740492097420929892490974202241981098409840984091564810684565416840659648198410651068657103974645132104164354016450306480360340639740650049510942094209420974219704902741950491209742109742097421902740927404920974209298924909742022419810984098409840915648106845654168406596481984106510686571039746451321041643540164503064803603406397406500495109420942094209742197049027419504912097421097420974219027409274049209742092989249097420224198109840984098409156481068456541684065964819841065106865710397464513210416435401645030648036034063974065004951094209420942097421970490274195049120974210974209742190274092740492097420929892490974202241", ctx), kk_integer_from_str("98109840984098409156481068456541684065964819841065106865710397464513210416435401645030648036034063974065004951094209420942097421970490274195049120974210974209742190274092740492097420929892490974202241", ctx), ctx), kk_integer_from_str("1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", ctx), ctx);
  //expect_eq(kk_integer_cdiv(kk_integer_from_str(e),kk_integer_from_str(d)),kk_integer_from_str("100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",ctx),ctx);
  expect_eq(kk_integer_cdiv(kk_integer_from_str("1e1050", ctx), kk_integer_from_str("1e1000", ctx), ctx), kk_integer_from_str("1e50", ctx), ctx);
  expect_eq(kk_integer_cdiv(kk_integer_from_str("650891045068740450350436540352434350243346254305240433565403624570436542564034355230360437856406345450735366803660233645540323657640436735034636550432635454032364560324366403643455063652403346540263364032643454530236455402336455640363263405423565405623454062354540326564062306456432664546654436564364556406435460643646363545606345066534456065340165344065234064564", ctx), kk_integer_from_str("2634565230452364554234565062345452365450236455423654456253445652344565423655423655462534506253450462354056523445062535462534052654350426355023654540625344056203455402635454026435501635446643754664546780646476442344654465764466744566754436556406235454066354570657548036545465", ctx), ctx), kk_integer_from_str("247058238507527885509216194910087226997858456323482112332514020694766925604284002588230023", ctx), ctx);
  expect_eq(kk_integer_cdiv(kk_integer_from_str("650891045068740450350436540352434350243346254305240433565403624570436542564034355230360437856406345450735366803660233645540323657640436735034636550432635454032364560324366403643455063652403346540263364032643454530236455402336455640363263405423565405623454062354540326564062306456432664546654436564364556406435460643646363545606345066534456065340165344065234064564000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", ctx), kk_integer_from_str("2634565230452364554234565062345452365450236455423654456253445652344565423655423655462534506253450462354056523445062535462534052654350426355023654540625344056203455402635454026435501635446643754664546780646476442344654465764466744566754436556406235454066354570657548036545465000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", ctx), ctx), kk_integer_from_str("247058238507527885509216194910087226997858456323482112332514020694766925604284002588230023", ctx), ctx);
  expect_eq(kk_integer_cdiv(kk_integer_from_str("9999999999999900000000000000", ctx), kk_integer_from_str("999999999999990000001", ctx), ctx), kk_integer_from_str("9999999", ctx), ctx);
  expect_eq(kk_integer_cdiv(kk_integer_from_str("1e9999", ctx), kk_integer_from_str("1e999", ctx), ctx), kk_integer_from_str("1e9000", ctx), ctx);
}


static void test_count(kk_context_t* ctx) {
  expect_eq(kk_integer_count_digits(kk_integer_from_int(0, ctx), ctx), kk_integer_from_int(1, ctx), ctx);
  expect_eq(kk_integer_count_digits(kk_integer_from_int(9999, ctx), ctx), kk_integer_from_int(4, ctx), ctx);
  expect_eq(kk_integer_count_digits(kk_integer_from_int(70123, ctx), ctx), kk_integer_from_int(5, ctx), ctx);
  expect_eq(kk_integer_count_digits(kk_integer_from_int(-70123, ctx), ctx), kk_integer_from_int(5, ctx), ctx);
  expect_eq(kk_integer_count_digits(kk_integer_from_str("1234567890", ctx), ctx), kk_integer_from_int(10, ctx), ctx);
  expect_eq(kk_integer_count_digits(kk_integer_neg(kk_integer_from_str(b, ctx), ctx), ctx), kk_integer_from_int(100, ctx), ctx);

  expect_eq(kk_integer_ctz(kk_integer_from_int(0, ctx), ctx), kk_integer_from_int(0, ctx), ctx);
  expect_eq(kk_integer_ctz(kk_integer_from_int(-9900, ctx), ctx), kk_integer_from_int(2, ctx), ctx);
  expect_eq(kk_integer_ctz(kk_integer_from_int(70000, ctx), ctx), kk_integer_from_int(4, ctx), ctx);
  expect_eq(kk_integer_ctz(kk_integer_from_str("100000000", ctx), ctx), kk_integer_from_int(8, ctx), ctx);
  expect_eq(kk_integer_ctz(kk_integer_from_str("10000000000", ctx), ctx), kk_integer_from_int(10, ctx), ctx);
  expect_eq(kk_integer_ctz(kk_integer_from_str("1000000000000000000", ctx), ctx), kk_integer_from_int(18, ctx), ctx);
}

static void test_pow10(kk_context_t* ctx) {
  expect_eq(kk_integer_mul_pow10(kk_integer_from_str("1234567890", ctx), kk_integer_from_int(0, ctx), ctx), kk_integer_from_str("1234567890", ctx), ctx);
  expect_eq(kk_integer_mul_pow10(kk_integer_from_int(0, ctx), kk_integer_from_str("1234567890", ctx), ctx), kk_integer_from_int(0, ctx), ctx);
  expect_eq(kk_integer_mul_pow10(kk_integer_from_int(-1, ctx), kk_integer_from_str("12", ctx), ctx), kk_integer_from_str("-1e12", ctx), ctx);
  expect_eq(kk_integer_mul_pow10(kk_integer_from_str("1234567890", ctx), kk_integer_from_int(8, ctx), ctx), kk_integer_from_str("1234567890e8", ctx), ctx);
  expect_eq(kk_integer_mul_pow10(kk_integer_from_str("-1234567890", ctx), kk_integer_from_int(8, ctx), ctx), kk_integer_from_str("-1234567890e8", ctx), ctx);
  expect_eq(kk_integer_mul_pow10(kk_integer_from_str("1234567890", ctx), kk_integer_from_int(18, ctx), ctx), kk_integer_from_str("1234567890e18", ctx), ctx);
  expect_eq(kk_integer_mul_pow10(kk_integer_from_int(1234, ctx), kk_integer_from_int(14, ctx), ctx), kk_integer_from_str("1234e14", ctx), ctx);

  expect_eq(kk_integer_cdiv_pow10(kk_integer_from_str("1234567890", ctx), kk_integer_from_int(0, ctx), ctx), kk_integer_from_str("1234567890", ctx), ctx);
  expect_eq(kk_integer_cdiv_pow10(kk_integer_from_int(0, ctx), kk_integer_from_str("1234567890", ctx), ctx), kk_integer_from_int(0, ctx), ctx);
  expect_eq(kk_integer_cdiv_pow10(kk_integer_from_str("-1e13", ctx), kk_integer_from_str("12", ctx), ctx), kk_integer_from_str("-10", ctx), ctx);
  expect_eq(kk_integer_cdiv_pow10(kk_integer_from_str("1234567890", ctx), kk_integer_from_int(8, ctx), ctx), kk_integer_from_str("12", ctx), ctx);
  expect_eq(kk_integer_cdiv_pow10(kk_integer_from_str("-1234567890", ctx), kk_integer_from_int(8, ctx), ctx), kk_integer_from_str("-12", ctx), ctx);
  expect_eq(kk_integer_cdiv_pow10(kk_integer_from_str("9999999999", ctx), kk_integer_from_int(8, ctx), ctx), kk_integer_from_str("99", ctx), ctx);
  expect_eq(kk_integer_cdiv_pow10(kk_integer_from_str("1234567890", ctx), kk_integer_from_int(18, ctx), ctx), kk_integer_from_int(0, ctx), ctx);
  expect_eq(kk_integer_cdiv_pow10(kk_integer_from_str("1234e14", ctx), kk_integer_from_int(14, ctx), ctx), kk_integer_from_str("1234", ctx), ctx);
}

static kk_integer_t ia;
static kk_integer_t ib;
static kk_integer_t ic;

static void init_nums(kk_context_t* ctx) {
  ia = kk_integer_from_str(a, ctx);
  ib = kk_integer_from_str(b, ctx);
  ic = kk_integer_from_str(c, ctx);
}

static kk_integer_t init_num(size_t  digits, kk_context_t* ctx) {
  char* s = (char*)kk_malloc(digits + 1, ctx);
  for (size_t i = 0; i < digits; i++) {
    s[i] = '0' + (9 - (i % 10));
  }
  s[digits] = 0;
  kk_integer_t x = kk_integer_from_str(s, ctx);
  kk_free(s, ctx);
  return x;
}

static void test_mul(kk_integer_t x, kk_integer_t y, kk_context_t* ctx) {
  kk_integer_t i = kk_integer_mul(x, y, ctx);
  kk_integer_drop(i, ctx);
}
/*
static void test_mulk(kk_integer_t x, kk_integer_t y) {
  kk_integer_t i = integer_mul_karatsuba(x, y);
  kk_integer_drop(i);
}
*/

static void test_bitcount(void) {
  uint32_t values[] = { 1,0x80000000,0xFFFFFFFF,0xFFFF,0xFFFF0000,0x7FFFFFFF,0xFFFFFFFE, 0x7FFFFFFE, 0x80000001, 0 };
  size_t i = 0;
  uint32_t v;
  int l, t;
  do {
    v = values[i++];
    l = kk_bits_clz32(v);
    t = kk_bits_ctz32(v);
    if (v == 0) assert((l + t) == 64);
    printf("value: 0x%08x, ctz: %2d, clz: %2d\n", v, t, l);
  } while (v != 0);
  for (v = 1; v != 0; v <<= 1) {
    l = kk_bits_clz32(v);
    t = kk_bits_ctz32(v);
    printf("value: 0x%08x, ctz: %2d, clz: %2d\n", v, t, l);
    assert((l + t) == 31);
  }
}


static void test_box_double(double dx, kk_context_t* ctx) {
  kk_box_t bx = kk_double_box(dx, ctx);
  double e = kk_double_unbox(bx, KK_BORROWED, ctx);
  printf("value: %.20e, box-unbox to: %.20e, box: 0x%016zx\n", dx, e, (intptr_t)bx.box);
  assert(e == dx || (isnan(e) && isnan(dx)));
}

static void test_double(kk_context_t* ctx) {
  double values[] = { 0.0, 1.0, 3.142, 0.5, 1.5, 2.5, -1.5, -2.5, pow(2.0,-510.0), pow(2.0,-511.0), pow(2.0,32.0), pow(2.0,64.0), pow(2.0,511.0), pow(2.0,512.0), pow(2.0,513.0), HUGE_VALL, pow(10.0,308), DBL_MAX, -DBL_MAX, DBL_EPSILON, (double)NAN };
  size_t i = 0;
  double dx;
  do {
    dx = values[i++];
    kk_integer_t x = kk_integer_from_double(dx, ctx);
    printf("value: %.20e, %.20e, integer: ", dx, round(dx)); kk_integer_print(x, ctx); printf("\n");
  } while (!isnan(dx));
  // test boxing
  i = 0;
  do {
    dx = values[i++];
    test_box_double(dx, ctx);
    test_box_double(nexttoward(dx, -HUGE_VALL), ctx);
    test_box_double(nexttoward(dx, HUGE_VALL), ctx);
    test_box_double(-dx, ctx);
    test_box_double(nexttoward(-dx, -HUGE_VALL), ctx);
    test_box_double(nexttoward(-dx, +HUGE_VALL), ctx);
  } while (!isnan(dx));
}

static void test_count10_64(uint64_t u) {
  uint8_t c = kk_bits_digits64(u);
  char buf[64];
  snprintf(buf, 63, "%" PRIuI64, u);
  if (strlen(buf) != c) {
    printf("*************\nvalue: %s: is not %i digits!!!\n************\n", buf, c);
  }
  else {
    printf("value: %s: digits: %i\n", buf, c);
  }
}

static bool test_count10_32(uint32_t u) {
  uint8_t c = kk_bits_digits32(u);
  char buf[64];
  snprintf(buf, 63, "%" PRIuI32, u);
  if (strlen(buf) != c) {
    printf("*************\nvalue: %s: is not %i digits!!!\n************\n", buf, c);
    return false;
  }
  else {
    printf("value: %s: digits: %i\n", buf, c);
    return true;
  }
}

static inline uint64_t kk_random_shuffle(uint64_t x) {
  if (x == 0) { x = 17; }   // ensure we don't get stuck in generating zeros
  x ^= x >> 30;
  x *= 0xbf58476d1ce4e5b9UL;
  x ^= x >> 27;
  x *= 0x94d049bb133111ebUL;
  x ^= x >> 31;
  return x;
}
/*
static void test_pdep64(void) {
  uint64_t state = 5381;
  printf("testing pdep64...\n");
  for (uint64_t i = 0; i < 100000000; i++) {
    uint64_t x = kk_random_shuffle(state);
    uint64_t mask = kk_random_shuffle(state);
    state = kk_random_shuffle(state);
    uint64_t res1 = kk_bits_scatter64(x, mask);
    uint64_t res2 = kk_bits_generic_scatter64(x, mask);
    if (res1 != res2) {
      printf("********\n error: pdep(%zi,%zi) = %zi != %zi\n**********\n", x, mask, res1, res2);
    }
    if (i % 100000 == 0) printf(".");
  }
  printf("ok.\n");
}

static void test_pext64(void) {
  uint64_t state = 5381;
  printf("testing pext64...\n");
  for (uint64_t i = 0; i < 100000000; i++) {
    uint64_t x = kk_random_shuffle(state);
    uint64_t mask = kk_random_shuffle(state);
    state = kk_random_shuffle(state);
    uint64_t res1 = kk_bits_gather64(x, mask);
    uint64_t res2 = kk_bits_generic_gather64(x, mask);
    if (res1 != res2) {
      printf("********\n error: pext(%zi,%zi) = %zi != %zi\n**********\n", x, mask, res1, res2);
    }
    if (i % 100000 == 0) printf(".");
  }
  printf("ok.\n");
}
*/

/*
static void test_mul64(kk_context_t* ctx) {
  uint64_t state = 5381;
  printf("testing umul64...\n");
  for (uint64_t i = 0; i < 100000000; i++) {
    uint64_t x = kk_random_shuffle(state);
    uint64_t y = kk_random_shuffle(state);
    state = kk_random_shuffle(state);
    uint64_t lo1, hi1; lo1 = kk_umul64_wide(x, y, &hi1);
    uint64_t lo2, hi2; lo2 = kk_umul64_msc_wide(x, y, &hi2);
    if (lo1 != lo2 || hi1 != hi2) {
      printf("********\n error: %zi * %zi = (%zi,%zi) != (%zi,%zi)\n**********\n", x, y, lo1, hi1, lo2, hi2);
    }
    if (i % 100000 == 0) printf(".");
  }
  printf("ok.\n");
}
*/

static void test_count10(kk_context_t* ctx) {
  {
    uint64_t u = 0;
    for (int i = 0; i < 22; i++) {
      test_count10_64(u - 1);
      test_count10_64(u);
      test_count10_64(u + 1);
      test_count10_64(u * 9);
      if (u == 0) u = 1;
      else u *= 10;
    }
    u = 1;
    for (int i = 0; i < 64; i++) {
      test_count10_64(u - 1);
      test_count10_64(u);
      test_count10_64(u + 1);
      test_count10_64(u * 9);
      if (u == 0) u = 1;
      else u <<= 1;
    }
  }
  {
    uint32_t u = 0;
    for (int i = 0; i < 11; i++) {
      test_count10_32(u - 1);
      test_count10_32(u);
      test_count10_32(u + 1);
      test_count10_32(u * 9);
      if (u == 0) u = 1;
      else u *= 10;
    }
    u = 1;
    for (int i = 0; i < 33; i++) {
      test_count10_32(u - 1);
      test_count10_32(u);
      test_count10_32(u + 1);
      test_count10_32(u * 9);
      if (u == 0) u = 1;
      else u <<= 1;
    }
  }
}

static void test_random(kk_context_t* ctx) {
  msecs_t start = _clock_start();
  uint32_t y = kk_srandom_uint32(ctx);
  const size_t N = 100000000;
  for (size_t i = 0; i < N; i++) {
    y = kk_srandom_range_uint32(60000, ctx);
  }
  msecs_t end = _clock_end(start);
  printf("chacha20: final: 0x%x, %6.3fs\n", y, (double)end / 1000.0);
}

static void test_ovf(kk_context_t* ctx) {
  /*
  add + subtract, 100000000x
  on x86-64, with a Xeon:
         portable  builtin   (overflow detection method)
  msvc:  0.185s    N/A
  gcc :  0.147s    0.105s
  clang: 0.175s    0.170s

  add only, 100000000x
  on x86-64, with a Xeon:
         portable  builtin   (overflow detection method)
  msvc:  0.102s    N/A
  gcc :  0.068s    0.054s
  clang: 0.067s    0.053s
  */
  msecs_t start = _clock_start();
  kk_integer_t n = kk_integer_zero;
  kk_intx_t i = 0;
#ifdef NDEBUG
  const kk_intx_t delta = 1;
#else
  const kk_intx_t delta = 100;
#endif
  for (; i < 100000000; i += delta) { n = kk_integer_inc(n, ctx); }
  for (; i > 0; i -= delta) { n = kk_integer_dec(n, ctx); }
  msecs_t end = _clock_end(start);
  kk_integer_print(n, ctx);
  printf("\nint-inc-dec: %6.3fs\n", (double)end / 1000.0);
}


// Use double-double type for high precision conversion from duration to two doubles.
typedef struct kk_ddouble_s {
  double hi;
  double lo;
} kk_ddouble_t;

static kk_ddouble_t kk_dd_sum(double x, double y) {
  double z = x + y;
  double diff = z - x;
  double err = (x - (z - diff)) + (y - diff);
  kk_ddouble_t dd = { z, err };
  return dd;
}

static kk_ddouble_t kk_dd_quicksum(double x, double y) {
  kk_assert(abs(x) >= abs(y));
  double z = x + y;
  double err = y - (z - x);
  kk_ddouble_t dd = { z, err };
  return dd;
}

static kk_ddouble_t kk_dd_add(kk_ddouble_t x, kk_ddouble_t y) {
  kk_ddouble_t z1 = kk_dd_sum(x.hi, y.hi);
  kk_ddouble_t low = kk_dd_sum(x.lo, y.lo);
  double e1 = z1.lo + low.hi;
  kk_ddouble_t z2 = kk_dd_quicksum(z1.hi, e1);
  double e2 = z2.lo + low.lo;
  return kk_dd_quicksum(z2.hi, e2);
}

static kk_ddouble_t kk_dd_from_int64(int64_t i, double scale) {
  double x = ((double)kk_sar64(i,32) * 0x1p32) * scale;
  double y = (double)((uint32_t)i) * scale;
  return kk_dd_sum(x, y);
}

#define KK_INT52_MAX  ((KK_I64(1)<<51) - 1)
#define KK_INT52_MIN  (-KK_INT52_MAX - 1)

static kk_ddouble_t kk_dd_from_duration(kk_duration_t d) {
  if kk_likely(d.attoseconds % KK_I64(1000000000) == 0) {
    int64_t nsecs = (d.attoseconds / KK_I64(1000000000));
    if ((int32_t)nsecs == nsecs) {
      double frac = ((double)nsecs * 1e-9);
      double secs = 0;
      if ((int32_t)secs == d.seconds) {
        secs = (double)d.seconds;
      }
      else {
        double sign = (d.seconds < 0 ? -1.0 : 1.0);
        int64_t s = (d.seconds < 0 ? -d.seconds : d.seconds);
        secs  = sign * ((double)kk_shr64(s, 16) * 0x1p16);
        frac  = frac + (sign * (double)((uint16_t)s) * 1e18);
      }
      kk_ddouble_t dd;
      dd.hi = secs;
      dd.lo = frac;
      return dd;
    }
  }

  if kk_likely((d.attoseconds % 1000) == 0 &&  // 1e-15 precision fits in 52 bits
               d.seconds >= KK_INT52_MIN && d.seconds < KK_INT52_MAX)
  {
    // fast path when both components can be converted directly with full precision
    kk_ddouble_t dd;
    dd.hi = (double)(d.seconds);
    dd.lo = (double)(d.attoseconds / 1000) * 1e-15;
    return dd;
  }
  else {
    // otherwise use ddouble arithmetic
    return kk_dd_add(kk_dd_from_int64(d.seconds, 1.0), kk_dd_from_int64(d.attoseconds, 1e-18));
  }
}


void kk_duration_to_ddouble(kk_duration_t d, double* psecs, double* pfrac) {
  kk_ddouble_t dd = kk_dd_from_duration(d);
  int64_t secs = d.seconds;
  int64_t asecs = d.attoseconds;
  int sbits = 64 - kk_bits_clz64((uint64_t)secs); // bits used by the seconds
  printf("duration: %20llus %lluas, sbits: %d, %20es . %fe, %.20es . %.20es\n", secs, asecs, sbits, (double)secs, (double)asecs * 1e-18, dd.hi, dd.lo);

  if (psecs != NULL) *psecs = dd.hi;
  if (pfrac != NULL) *pfrac = dd.lo;
}

void test_duration1(void) {
  for (int64_t i = 1; i < (INT64_MAX/2); i <<= 1) {
    kk_duration_t d;
    d.seconds = i;
    d.attoseconds = KK_I64(1000000000) * KK_I64(1000000000) - 1 - KK_I64(1000000000);
    kk_duration_to_ddouble(d, NULL, NULL);
    d.seconds += 1;
    d.attoseconds = KK_I64(1000000000);
    kk_duration_to_ddouble(d, NULL, NULL);
  }
}

int main() {
  kk_context_t* ctx = kk_get_context();

  test_fib(50, ctx);   // 12586269025
  test_fib(150, ctx);  // 9969216677189303386214405760200
  test_fib(300, ctx);  // 22223224462942044552973989346190996720666693909649976499097960
  test_read("123456789", ctx);
  test_read("12586269025", ctx);
  test_read("99692166771893033862144057602", ctx);
  test_read("22223224462942044552973989346190996720666693909649976499097960", ctx);
  test_cmp_pos(ctx);
  test_addx(ctx);
  test_carry(ctx);
  test_large(ctx);
  test_cdiv(ctx);
  test_count(ctx);
  test_pow10(ctx);
  test_double(ctx);
  test_ovf(ctx);

  test_count10(ctx);
  test_bitcount();

  //test_popcount();

  //test_random(ctx);
  //test_duration1();

  //test_pdep64();
  //test_pext64();
  //test_mul64(ctx);


  /*
  init_nums();
  for (int i = 100; i < 800; i+=50) {
    kk_integer_t x = init_num(i);
    for (int j = 100; j < 800; j += 50) {
      kk_integer_t y = init_num(j);
      printf(".");
      msecs_t t1 = test_timing("multiply l", 10000, test_mul, integer_dup(x), integer_dup(y));
      msecs_t t2 = test_timing("multiply k", 10000, test_mulk, integer_dup(x), integer_dup(y));
      //printf("test digit count: %i x %i: predict: %s\n", i, j, (use_karatsuba(i, j) ? "karatsuba" : "long"));
      if (use_karatsuba(i,j) != (t1 >(t2 + (t2/50)))) {
        printf("\n***: %i x %i, predict: %s, but winner was: %s (%3.3f s vs %3.3f s)\n", i, j, (use_karatsuba(i, j) ? "karatsuba" : "long"), (t1 >(t2 + (t2/50))) ? "karatsuba" : "long", t1/1000.0, t2/1000.0 );
      }
      kk_integer_drop(y);
    }
    kk_integer_drop(x);
  }
  */

  puts("Success!");
  return 0;
}

/*

inline ptr acquire(ptr p) {
  block_of(p)->header.refcount_full++;
  return p;
}

inline ptr shallow_release(ptr p) {
  block_t* b = block_of(p);
  b->header.refcount_full--;
  if (b->header.refcount_lo == 0 && b->header.refcount_hi == 0) {
    kk_free(b);
  }
  return p;
}

inline ptr release(ptr p) {
  shallow_release(p);
}

inline boxed boxed_acquire(boxed b) {
  if (kk_box_is_ptr(b)) {
    acquire(kk_ptr_unbox(b));
  }
  return b;
}

inline boxed boxed_release(boxed b) {
  if (kk_box_is_ptr(b)) {
    release(kk_ptr_unbox(b));
  }
  return b;
}

inline bool _check_reuse(ptr p, tag_t tag) {
  if (p == 0) return false;
  assert(tag_of(p) == tag);
  block_t* b = block_of(p);
  if (b->header.refcount_lo==1 && b->header.refcount_hi==0) {
    return true;
  }
  else {
    assert(b->header.refcount > 1);
    b->header.refcount_full--;
    return false;
  }
}

#define alloc_tp_reuse(tp,reuse,n,tag)   (_check_reuse(reuse,tag) ? tp_of(tp,reuse) : alloc_tp(tp,n,tag));

#define list_tag_nil  1
#define list_tag_cons 2

struct list_nil_t { header_t header;  };

ptr list_nil() {
  static struct list_nil_t _nil = { KK_HEADER(list_tag_nil) };
  return acquire(make_ptr(&_nil,list_tag_nil));
}

struct list_cons_t { boxed head; ptr tail; };

ptr list_cons(ptr reuse, boxed head, ptr tail) {
  struct list_cons_t* c = alloc_tp_reuse(struct list_cons_t, reuse, 2, list_tag_cons);
  c->head = head;
  c->tail = tail;
  return make_ptr(c, list_tag_cons);
}

typedef ptr function;

boxed apply(function f, boxed arg) {
  return arg;
}

bool unwind = false;

ptr map(function f, ptr xs) {
  if (tag_of(xs) == list_tag_cons) {
    struct list_cons_t* cons = tp_of(struct list_cons_t, xs);
    boxed x = cons->head;
    ptr xx  = cons->tail;
    boxed y = apply(acquire(f), x);   if (unwind) { release(f); release(xx); shallow_release(xs);  return 0; }
    ptr ys  = map(f, xx);             if (unwind) { release(y); shallow_release(xs); return 0; };
    return list_cons(xs, y, ys);
  }
  else {
    release(xs);
    return nil();
  }
  return 0;
}

kk_integer_t test_add(kk_integer_t x) {
  kk_integer_t res = int_add(x, int_small(4));
  return res;
}

int main(int argc, char** argv ) {
  kk_integer_t i = test_add(int_small(argc));
  if (i) printf("uh oh\n");
      else printf("hello world!\n");
}
*/
